﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Xml;
using Microsoft.Office.Server.Search.Administration;
using Microsoft.Office.Server.Search.Cmdlet;
using Microsoft.Office.Server.Search.Query;
using Microsoft.SharePoint.PowerShell;
using Microsoft.SharePoint.Search.Extended.Administration;
using Schema = Microsoft.SharePoint.Search.Extended.Administration.Schema.Schema;

namespace Community.SharePoint.PowerShell
{
    /// <summary>
    /// SharePoint PowerShell Community Kit
    /// Get-SPEnterpriseSearchQueryRefinement
    /// This PowerShell cmdlet tests a query of SharePoint Enterprise Search and
    /// returns an XmlDocument with refinements.
    /// Example (Queries Enterprise Search for the term "accounting" and saves the results as an XML file): 
    /// $searchapp = Get-SPEnterpriseSearchServiceApplication "FAST Query SSA"
    /// (Get-SPEnterpriseSearchQueryRefinement -SearchApplication $searchapp -p "FAST Query SSA" -l "FASTIndex" -query "accounting").Save("c:\scripts\SearchResults.xml")
    /// Author: Mikael Svenson (@mikaelsvenson)
    /// Creation Date: 2/22/2011
    /// </summary>
    [Cmdlet(VerbsCommon.Get, "SPEnterpriseSearchQueryRefinement")]
    public class GetEnterpriseSearchQueryRefinement : SPGetCmdletBase<XmlDocument>
    {
        private const string DefaultLocationName = "LocalSearchIndex";
        private const string DefaultSearchServiceApplicationProxyName = "Search Service Application";

        private string _location;
        private string _searchServiceApplicationProxyName;
        private string _refiners;

        [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)]
        public SearchServiceApplicationPipeBind SearchApplication { get; set; }

        [Parameter(Mandatory = false, Position = 1), Alias(new[] { "r" })]
        public string Refiners
        {
            get
            {
                if (string.IsNullOrEmpty(_refiners))
                {
                    SchemaContext schemaContext = new SchemaContext();
                    Schema schema = schemaContext.Schema;
                    var refiners =
                        schema.AllManagedProperties.Where(m => m.RefinementEnabled).Select(
                            managedProperty => managedProperty.Name.ToLower()).ToArray();
                    _refiners = string.Join(",", refiners);
                }
                return _refiners;
            }
            set { _refiners = value; }
        }

        [Parameter(Mandatory = true, Position = 1), Alias(new[] { "q" })]
        public string Query { get; set; }

        [Parameter(Mandatory = false, Position = 2), Alias(new[] { "l" }), DefaultValue(DefaultLocationName)]
        public string Location
        {
            get
            {
                return !string.IsNullOrEmpty(_location) ? _location : DefaultLocationName;
            }
            set { _location = value; }
        }

        [Parameter(Mandatory = false, Position = 3), Alias(new[] { "p" }), DefaultValue(DefaultSearchServiceApplicationProxyName)]
        public string SearchServiceApplicationProxyName
        {
            get
            {
                return !string.IsNullOrEmpty(_searchServiceApplicationProxyName) ? _searchServiceApplicationProxyName : DefaultSearchServiceApplicationProxyName;
            }
            set { _searchServiceApplicationProxyName = value; }
        }

        /// <summary>
        /// Return an XmlDocument with search results.
        /// </summary>
        /// <returns></returns>
        protected override IEnumerable<XmlDocument> RetrieveDataObjects()
        {
            // get the service application
            SearchServiceApplication searchServiceApplication = SearchApplication.Read();

            // get the search service application proxy - there is probably a better way to do this
            var searchProxy =
                (SearchServiceApplicationProxy)
                searchServiceApplication.ServiceApplicationProxyGroup.Proxies.FirstOrDefault(
                    p => p.Name == SearchServiceApplicationProxyName);

            using (KeywordQuery query = new KeywordQuery(searchProxy))
            {
                query.QueryText = Query;
                query.ResultTypes = ResultType.RefinementResults;
                query.Refiners = Refiners;
                ResultTableCollection resultTableCollection = query.Execute();
                ResultTable resultTable = resultTableCollection[ResultType.RefinementResults];
                StringBuilder sb = new StringBuilder();
                XmlWriter writer = XmlWriter.Create(sb);
                writer.WriteStartElement("refiners");

                string lastRefinerName = string.Empty;
                foreach (DataRow row in resultTable.Table.Rows)
                {
                    string refinerName = (string)row["RefinerName"];
                    if (lastRefinerName != refinerName)
                    {
                        if (lastRefinerName != string.Empty)
                        {
                            writer.WriteEndElement();
                        }
                        writer.WriteStartElement("refiner");
                        writer.WriteAttributeString("name", refinerName);
                    }
                    writer.WriteStartElement("refinement");
                    writer.WriteAttributeString("count", row["RefinementCount"].ToString());
                    writer.WriteString(row["RefinementName"].ToString());
                    writer.WriteEndElement();
                    lastRefinerName = refinerName;
                }
                writer.WriteEndElement();
                writer.WriteEndElement();
                writer.Flush();
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.LoadXml(sb.ToString());
                return new List<XmlDocument> { xmlDocument };
            }
        }
    }
}